Java基础知识(十三)

Author Avatar
子语 2017 - 10 - 28
  • 在其它设备中阅读本文章

接口

接口的基本定义

1、接口是只由抽象方法和全局变量组成的类,使用interface关键字定义。

interface A { // 定义接口
    public static final String MSG = "Hello"; // 全局常量

    public abstract void print(); // 抽象方法
}

由于接口中存在抽象方法,因此接口对象不能直接实例化,其使用原则如下:

(1)接口必须要有子类,子类可以使用implements关键字实现多接口;
(2)接口的子类(非抽象类时),必须对接口的抽象方法进行覆写;
(3)接口的对象可以利用子类对象通过向上转型实现实例化。

interface A { // 定义接口
    public static final String MSG = "Hello"; // 全局常量

    public abstract void print(); // 抽象方法
}

interface B {
    public abstract void get();
}

class X implements A, B { // X 实现A和B两个接口
    public void print() {
        System.out.println("A.抽象方法");
    }

    public void get() {
        System.out.println("B.抽象方法");
    }
}

public class Demo {
    public static void main(String[] args) {
        X x = new X(); // 实例化子类对象
        A a = x; // 向上转型
        B b = x; // 向上转型
        a.print();
        b.get();
    }
}

X是A和B的子类,因此X的对象可以变为A或者B接口的对象。

public class Demo {
    public static void main(String[] args) {
        A a = new X();
        B b = (B) a;
        b.get(); // B.抽象方法
        System.out.println(a instanceof A); // true
        System.out.println(b instanceof B); // true
    }
}

从结构上来说,A和B接口没有直接关系,但是两个接口拥有共同的子类X,最终实例化的是X,这个子类向上转型为B类对象,因此代码可以执行。
2、子类同时继承类和接口时,语法:class X extends A implements B {}
接口的组成只有抽象方法和全局变量,因此可以不写abstractpublic static final,并且方法是否使用public定义都是一样的,因为接口只能使用public权限。

|interface A{ 
    public static final String MSG = "Hello"; //全局常量
	public abstract void print(); //抽象方法
}	
|interface A{
    String MSG = "Hello"; //全局常量
	void print(); //抽象方法
}

上述代码是等价的。在接口中,默认访问权限为public,而不是default。但开发时,定义接口中的方法时要写上public。
3、一个抽象类只能继承一个抽象类,但一个接口可以使用extends关键字同时继承多个接口,接口不能继承抽象类。

interface A {
    public void funA();
}

interface B {
    public void funB();
}

interface C extends A, B {
    public void funC();
}

class X implements C {
    public void funA() {
    }

    public void funB() {
    }

    public void funC() {
    }
}

从继承上来说,抽象类的限制比接口多:

(1)一个抽象类只能继承一个抽象父类,而接口没有该限制;
(2)一个子类只能继承一个抽象类,但可以实现多接口。

因此Java中接口的功能是解决单继承限制
4、从概念而言,接口只能由抽象方法和全局变量组成。但接口中可以定义普通内部类、抽象内部类、内部接口。这些内部接口不受接口的概念限制。

interface A {
    public void funA();

    abstract class B { // 定义抽象内部类
        public abstract void funB();
    }
}

class X implements A { // X实现A接口
    public void funA() {
    }

    class Y extends B { // 抽象内部类的子类
        public void funB() {
        }
    }
}

上述代码形式,几乎不会用到。
5、在接口中使用static定义一个内部接口,相当于外部接口。

interface A {
    public void funA();

    static interface B { // 相当于外部接口
        public void funB();
    }
}

class X implements A.B { // X实现B接口
    public void funB() {
    }
}

总结:
接口在实际开发中的三大作用:
(1)定义不同层之间的操作标准;
(2)表示一种操作的能力;
(3)表示将服务器端的远程方法视图暴露给客户端。

定义标准

无法加载
根据上图编写代码:电脑利用USB接口标准和其他设备关联。
范例:定义USB标准

// 标准可以连接不同层的操作类
interface USB { // 接口就是标准
    public void start();
    public void stop();
}

范例:定义电脑

class Computer{
    public void plugin(USB usb){ // 插入USB设备
        usb.start();
        usb.stop();
    }
}

不论是什么设备,只要实现了USB标准,就可以在电脑上使用。
范例:定义U盘

class Flash implements USB {
    public void start() {
        System.out.println("U盘开始使用");
    }

    public void stop() {
        System.out.println("U盘停止使用");
    }
}

范例:定义打印机

class Print implements USB {
    public void start() {
        System.out.println("打印机开始使用");
    }

    public void stop() {
        System.out.println("打印机停止使用");
    }
}

范例:测试

public class Demo {
    public static void main(String[] args) {
        Computer com = new Computer();
        com.plugin(new Flash());
        com.plugin(new Print());
    }
}

Java中,标准就是被定义为接口

工厂设计模式

interface Fruit {
    public void eat();
}

class Apple implements Fruit {
    public void eat() {
        System.out.println("吃苹果");
    }
}

上述代码通过主方法可以取得Fruit对象,但存在如下问题:

附:要确定代码是否合理,标准如下:
(1)客户端调用简单,不用关注具体实现;
(2)客户端之外的代码修改,不影响用户的使用,即用户不用关注代码的变更。

由上述标准可知该程序的问题是一个接口不一定只有子类,现在添加一个Fruit子类:

class Orange implements Fruit {
    public void eat() {
        System.out.println("吃橘子");
    }
}

客户端要使用这个新子类,需要修改代码:

public class Demo {
    public static void main(String[] args) {
        Fruit f = new Orange ();
        f.eat(); // 吃橘子
    }
}

在上述代码,我们最关注的是如何获得Fruit对象,之后进行方法调用。至于该对象是怎么实例的,不是客户端的工作。
1、上述程序中,客户端每次更换对象,就需要修改主方法的代码,这不符合标准。该问题的关键在于new的使用,这种问题是因为耦合度太高(两者联系程度太高)耦合度太高,代码不便于维护

解决思路:参考Java虚拟机的设定-程序→JVM→适应不同的操作系统。

范例:增加一个过渡

class Factory { // 工厂设计模式
    public static Fruit getInstance(String className) {
        if ("apple".equals(className)) {
            return new Apple();
        } else if ("orange".equals(className)) {
            return new Orange();
        } else {
            return null;
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        Fruit f = Factory.getInstance("apple");
        f.eat(); // 吃苹果
    }
}

此时,所有子类对于客户端是不可见的,因为Fruit对象的实例化是通过Factory对象获取的,日后扩充Fruit子类时,只需修改Factory即可,不用修改客户端代码。
无法加载
上图描述的是工厂设计模式,客户端可见的只有接口和Factory。

代理设计模式

无法加载
代理设计模式的核心在于有一个主题操作接口(可能有很多接口),核心操作类只完成核心功能,而代理主题负责完成所有与核心操作有关的辅助性操作。
1、以去餐馆吃饭为例,外人只看到吃饭,看不到厨师和顾客。皇帝完成核心业务,厨师完成辅助业务。
无法加载

package com.java.demo;

interface Subject {  // 核心业务
    public void eat();
}

class RealSubject implements Subject {   // 实际进行核心业务的对象
    public void eat() {
        System.out.println("顾客在吃饭");
    }
}

class ProxySubject implements Subject {  // 执行代理操作的对象
    private Subject subject;  // 接收进行核心业务的对象

    public ProxySubject(Subject subject) {
        this.subject = subject;
    }

    public void prepared() {
        System.out.println("为吃做准备");
    }

    public void eat() {
        this.prepared();
        this.subject.eat();
        this.destory();
    }

    public void destory() {
        System.out.println("为吃收尾");
    }
}

public class Demo {
    public static void main(String[] args) {
        Subject sub = new ProxySubject(new RealSubject());
        sub.eat();  // 调用代理操作
    }
}

接口与抽象类的区别

No. 区别 抽象类 接口
1 关键字 abstract class interface
2 组成 构造方法、普通方法、抽象方法、static方法、变量、常量 抽象方法,全局常量
3 子类使用 class 子类 extends 抽象类 class 子类implements 接口,接口…
4 关系 抽象类可以实现多接口 接口不能继承抽象类,却可以继承多个接口
5 权限 可以使用任意权限 只能使用public权限
6 限制 单继承局限 没有单继承局限
7 子类 抽象类和接口都必须有子类,子类必须要覆写全部抽象方法 抽象类和接口都必须有子类,子类必须要覆写全部抽象方法
8 实例化对象 依靠子类对象的向上转型进行对象的实例化 依靠子类对象的向上转型进行对象的实例化

经过比较发现,抽象类存在单继承限制,因此当抽象类和接口都可以使用时,优先考虑接口。

(1)当进行公共操作时,要定义接口;
(2)有了接口就需要使用子类完善方法
(3)自定义的接口,不能直接实例化接口子类,应使用工厂设计模式。

This blog is under a CC BY-NC-SA 3.0 Unported License
本文链接:http://yov.oschina.io/article/Java/Java Base/Java基础知识(十三)/